#include "MyRSA.h"
#include<cstring>

CMyRSA::CMyRSA(void)
{
}

CMyRSA::~CMyRSA(void)
{
}
static char encoding_table[] = {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
'w', 'x', 'y', 'z', '0', '1', '2', '3',
'4', '5', '6', '7', '8', '9', '+', '/'};

static int mod_table[] = {0, 2, 1};

unsigned char oaep_int_seed[21];

int CMyRSA::HandleRsaEncrypt( char *&pEncryptBuf, 
							 unsigned int &uiEncryptLen, 
							 const char* pOrgBuf,      // 
							 unsigned int uiOrgLen, // uiOrgLen
							 const string &csCertFilePath )
{
	BIO *in = NULL;
	EVP_PKEY * pPubKeyPem = 0;
	X509 *x509Pem = NULL;

	if ((in = BIO_new_file (csCertFilePath.c_str(), "r")) == 0)
	{
		return -1;
	}

	x509Pem = PEM_read_bio_X509(in, NULL, 0, NULL);
	if (x509Pem == 0)
	{
		return -1;
	}

	pPubKeyPem = X509_get_pubkey(x509Pem);
	if (pPubKeyPem == 0) 
	{
		if (x509Pem) 
		{
			X509_free(x509Pem);
		}
		return -1;
	}
	else if (pPubKeyPem->pkey.rsa == 0)
	{
		if (x509Pem) 
		{
			X509_free(x509Pem);
		}
		return -1;
	}

	const char * pData = pOrgBuf;

	// 分配存储密文的缓存区
	unsigned int uiTotalSize = (uiOrgLen/100 + 2) * 128;
	pEncryptBuf = new char[uiTotalSize];
	memset(pEncryptBuf, 0, uiTotalSize);

	// 分配用来存储一段加密的临时缓冲区
	unsigned int nRsaSize = RSA_size(pPubKeyPem->pkey.rsa); // 获得临时缓存区的长度
	unsigned char* pEncrpted = new unsigned char[nRsaSize];

	int nInLen = 0;  // 需要加密的段的长度
	uiEncryptLen = 0; // 加密缓冲区的已经加密的长度
	for (size_t n = 100; n - 100 < uiOrgLen; n += 100)
	{
		// 获得这次加密的明文的长度（一般为100，除了最后一段）
		nInLen = uiOrgLen / n == 0 ? uiOrgLen - (n - 100) : 100;

		// 给这段明文加密到pEncrpted缓冲区，并获得加密后的实际长度
		int nTmp = RSA_public_encrypt(nInLen,(const unsigned char*)pData,pEncrpted,pPubKeyPem->pkey.rsa,RSA_PKCS1_PADDING);

		// 复制加密后的密文到加密缓存区
		memcpy(pEncryptBuf + uiEncryptLen, pEncrpted, nTmp);

		// 获得加密缓冲区的下一次加密的起始位置
		uiEncryptLen += nTmp;

		// 更新未加密的明文的起始位置
		pData += nInLen;
	}

	if (x509Pem) 
	{
		X509_free(x509Pem);
	}

	delete []pEncrpted;

	return 0;
}

int CMyRSA::RsaEncrypt(char *&pEncryptBuf, 
					   unsigned int &uiEncryptLen, 
					   const char* pOrgBuf, 
					   unsigned int uiOrgLen, 
					   const string &sEncrypt_list)
{	 
	string strPublicKey = sEncrypt_list; 
	int nPublicKeyLen = strPublicKey.size();      //strPublicKey为base64编码的公钥字符串
	int nLen = 0;
	for(int i = 64; i < nPublicKeyLen+nLen; i+=64)
	{
		if(strPublicKey[i] != '\n')
		{
			strPublicKey.insert(i, "\n");
			nLen++;
		}
		i++;
	}
	strPublicKey.insert(0, "-----BEGIN PUBLIC KEY-----\n");
	strPublicKey.append("\n-----END PUBLIC KEY-----\n");

	BIO *in = NULL; 
	char *chPublicKey = const_cast<char *>(strPublicKey.c_str());
	if ((in = BIO_new_mem_buf(chPublicKey, -1)) == NULL)       //从字符串读取RSA公钥
	{     
		return -1;   
	}       


	RSA* rsa = PEM_read_bio_RSA_PUBKEY(in, NULL, NULL, NULL);   //从bio结构中得到rsa结构
	if (!rsa)
	{
		BIO_free(in); 
		return -1;
	}
	//char szBuf[4000] = {0};
	//size_t nSize = BIO_ctrl_pending(in);  // 用这个函数可以获取到BUF里面的大小
	//BIO_read(in, szBuf, nSize);

	const char * pData = pOrgBuf;

	// 分配存储密文的缓存区
	unsigned int uiTotalSize = (uiOrgLen/100 + 2) * 128;
	pEncryptBuf = new char[uiTotalSize];
	memset(pEncryptBuf, 0, uiTotalSize);

	// 分配用来存储一段加密的临时缓冲区
	unsigned int nRsaSize = RSA_size(rsa); // 获得临时缓存区的长度
	unsigned char* pEncrpted = new unsigned char[nRsaSize];

	int nInLen = 0;  // 需要加密的段的长度
	uiEncryptLen = 0; // 加密缓冲区的已经加密的长度
	for (size_t n = 100; n - 100 < uiOrgLen; n += 100)
	{
		// 获得这次加密的明文的长度（一般为100，除了最后一段）
		nInLen = uiOrgLen / n == 0 ? uiOrgLen - (n - 100) : 100;

		// 给这段明文加密到pEncrpted缓冲区，并获得加密后的实际长度
		int nTmp = RSA_public_encrypt(nInLen,(const unsigned char*)pData,pEncrpted,rsa,RSA_PKCS1_PADDING);

		// 复制加密后的密文到加密缓存区
		memcpy(pEncryptBuf + uiEncryptLen, pEncrpted, nTmp);

		// 获得加密缓冲区的下一次加密的起始位置
		uiEncryptLen += nTmp;

		// 更新未加密的明文的起始位置
		pData += nInLen;
	}

	RSA_free(rsa);  
	BIO_free_all(in);
	delete []pEncrpted;

	return 0;
}

int CMyRSA::RsaEncrypt(char *&pEncryptBuf,const char* src,const char* rsan,const char* rsae,const char*rsal)
{ 
	init();   
 
	RSA * pubkey = RSA_new();
	int length ; 
 
	unsigned char dest[600]={0} ;

	length = base64decode(dest, (unsigned char*)rsan, strlen(rsan));

	pubkey->n = BN_bin2bn(dest, length, pubkey->n);
	 
	length = base64decode(dest, (unsigned char*)rsae, strlen(rsae));

	pubkey->e = BN_bin2bn(dest, length, pubkey->e);
   
	unsigned char ciphertext[600];

	int enclen = rsaes_oaep_enc((const unsigned char*)src, strlen(src),(const unsigned char*)rsal, strlen(rsal), pubkey,ciphertext);
	if (enclen == -1)
	{
		return -1;
	}
 
	unsigned char base64_msg[600] = {0};

	base64encode(base64_msg, ciphertext, enclen);

	int nLen = strlen((const char*)base64_msg);
	pEncryptBuf = new char[nLen+1];
	memcpy(pEncryptBuf, base64_msg, nLen);
	pEncryptBuf[nLen] = 0;
 
	return 0;
} 


int CMyRSA::sha1(const unsigned char * msg, unsigned int msg_len, unsigned char *digest)
{
	EVP_MD_CTX c;
	EVP_MD_CTX_init(&c);
	EVP_Digest(msg,msg_len,digest,NULL,EVP_sha1(), NULL);
	EVP_MD_CTX_cleanup(&c);

	return 0;
}

void CMyRSA::init()
{ 
	srand(time(0)); 	
	for (int i = 0; i< 20; i++) {
		oaep_int_seed[i] = rand()%0x100;
	}
	oaep_int_seed[20]=0; 
}

int CMyRSA::MGF1(unsigned char *mask, long len, const unsigned char *seed,
				 long seedlen)
{
	return PKCS1_MGF1(mask, len, seed, seedlen, EVP_sha1());
}

char CMyRSA::val2hex(int val)
{
	if(val<0xa)
		return val + '0';
	return val - 0xa + 'A';
}


void CMyRSA::dbg_mem_info(const unsigned char* mem, int len, const char* fmt, ...)
{
	va_list args;

	va_start(args,fmt);
	vprintf(fmt, args);
	va_end(args);
	printf("\n");
	dump(len, mem);
	printf("\n");
}

void CMyRSA::dump(int len, const unsigned char* str)
{
	int i;
	for(i = 0; i< len; i++)
	{
		if( (i&0xF) != 0xF)
			printf("%c%c ", val2hex((str[i]>>4)&0xF), val2hex(str[i]&0xF));
		else
			printf("%c%c\n", val2hex((str[i]>>4)&0xF), val2hex(str[i]&0xF));
	}

	printf("\n");
}

int CMyRSA::rsaes_oaep_enc(const unsigned char * msg, unsigned int msg_len, const unsigned char * label, unsigned int label_len, RSA * key, unsigned char* output)
{
	int i;
	int ret;

	unsigned char EM[600];
	unsigned char *db = &EM[1 + SHA_DIGEST_LENGTH];
	unsigned char *seed = &EM[1];
	int seed_len;
	unsigned char dbMask[600];
	unsigned char seedMask[600];
	int rsa_n_len = BN_num_bytes(key->n);//杩斿洖n鐨勪綅鏁?= 128

	printf("rsa_n_len is :%d\n",rsa_n_len);

	dbg_mem_info(msg, msg_len, "the message is:");


	//Step.1
	if(msg_len > rsa_n_len - 2 * SHA_DIGEST_LENGTH - 1)
	{
		printf("Message is too long\n");
		return -1;  
	}

	//Step.2.a, Step.2.b, Step.2.c
	printf("the label is:\n");
	dump(label_len,label);

	sha1(label,label_len, db);

	printf("lHash is:\n");
	dump(SHA_DIGEST_LENGTH, db);
	printf("\n");


	memset(&db[SHA_DIGEST_LENGTH], 0, rsa_n_len - SHA_DIGEST_LENGTH - 1 - msg_len);
	db[rsa_n_len - SHA_DIGEST_LENGTH -1 - msg_len - 1] = 0x01;
	memcpy(&db[rsa_n_len - SHA_DIGEST_LENGTH - 1 - msg_len ], msg, msg_len);


	printf("DB is:\n");
	dump(rsa_n_len  - SHA_DIGEST_LENGTH - 1 , db);
	printf("\n");

	memcpy(seed,oaep_int_seed, sizeof(oaep_int_seed)-1);
	seed_len = sizeof(oaep_int_seed) -1;


	printf("seed is:\n");
	dump(seed_len, seed);
	printf("\n");

	MGF1(dbMask,rsa_n_len - SHA_DIGEST_LENGTH - 1, seed, seed_len);


	printf("DBMask is:\n");
	dump(rsa_n_len - SHA_DIGEST_LENGTH - 1, dbMask);
	printf("\n");

	for(i=0; i<rsa_n_len - SHA_DIGEST_LENGTH - 1 ; i++)
		db[i] ^= dbMask[i];

	printf("MaskedDB is:\n");
	dump(rsa_n_len - SHA_DIGEST_LENGTH - 1, db);
	printf("\n");

	MGF1(seedMask, SHA_DIGEST_LENGTH , db, rsa_n_len - SHA_DIGEST_LENGTH -1 );

	printf("SeedMask is:\n");
	dump(SHA_DIGEST_LENGTH , seedMask);
	printf("\n");


	for(i = 0; i < SHA_DIGEST_LENGTH; i++)
	{
		seed[i] ^= seedMask[i];
	}

	printf("MaskedSeed is:\n");
	dump(SHA_DIGEST_LENGTH , seed);
	printf("\n");

	EM[0] = 0;


	printf("EM is:\n");
	dump(rsa_n_len , EM);
	printf("\n");


	ret = RSA_public_encrypt(rsa_n_len,EM,output,key,RSA_NO_PADDING );


	printf("Ciphertext is:\n");
	dump(rsa_n_len , output);
	printf("\n");

	return ret;
}

int CMyRSA::base64encode(unsigned char* dest,
						 const unsigned char *data,
						 int input_length)
{
	int i,j;
	int ret;

	unsigned int octet_a;
	unsigned int octet_b;
	unsigned int octet_c;

	unsigned int triple;

	ret = 4 * ((input_length + 2) / 3);

	if (dest == NULL) return -1;

	for (i = 0, j = 0; i < input_length;) {

		octet_a = i < input_length ? data[i++] : 0;
		octet_b = i < input_length ? data[i++] : 0;
		octet_c = i < input_length ? data[i++] : 0;

		triple = (octet_a << 0x10) + (octet_b << 0x08) + octet_c;

		dest[j++] = encoding_table[(triple >> 3 * 6) & 0x3F];
		dest[j++] = encoding_table[(triple >> 2 * 6) & 0x3F];
		dest[j++] = encoding_table[(triple >> 1 * 6) & 0x3F];
		dest[j++] = encoding_table[(triple >> 0 * 6) & 0x3F];
	}

	for (i = 0; i < mod_table[input_length % 3]; i++)
		dest[ret - 1 - i] = '=';
	dest[ret] = 0;

	return ret;
}

static const char  base64_symbl_table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
char CMyRSA::symbl_value(char c)
{
	const char *p = strchr(base64_symbl_table, c);
	if(p) {
		return p-base64_symbl_table;
	} else {
		return -1;
	}
}

int CMyRSA::base64decode(unsigned char *dest, const unsigned char *src, int srclen)
{
	*dest = 0;
	if(*src == 0)
	{
		return 0;
	}
	unsigned char *p = dest;
	do
	{

		char a = symbl_value(src[0]);
		char b = symbl_value(src[1]);
		char c = symbl_value(src[2]);
		char d = symbl_value(src[3]);
		*p++ = (a << 2) | (b >> 4);
		*p++ = (b << 4) | (c >> 2);
		*p++ = (c << 6) | d;
		if(symbl_value(src[1]) < 0)
		{
			p -= 2;
			break;
		}
		else if(symbl_value(src[2]) < 0)
		{
			p -= 2;
			break;
		}
		else if(symbl_value(src[3]) < 0)
		{
			p--;
			break;
		}
		src += 4;
		while(*src && (*src == 13 || *src == 10)) src++;
	}
	while(srclen -= 4);
	*p = 0;
	return p-dest;
}
